﻿using MSharp.Data.SPI;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace MSharp.Data.DatabaseInfo
{
    public class MySqlDBInfo : IDBInfo
    {
        private DB Db { get; set; }

        public MySqlDBInfo(DB db)
        {
            this.Db = db;
            Refresh();
        }


        public string DBName
        {
            get { return (Db.ConnectionStringBuilder as MySql.Data.MySqlClient.MySqlConnectionStringBuilder).Database; }
        }

        public NameValueCollection TableComments { get; private set; }
        public List<string> TableNames { get; private set; }
        public Dictionary<string, TableInfo> TableInfoDict { get; private set; }
        public Dictionary<string, List<string>> TableColumnNameDict { get; private set; }
        public Dictionary<string, List<ColumnInfo>> TableColumnInfoDict { get; private set; }

        public Dictionary<string, NameValueCollection> TableColumnComments { get; private set; }

        private Dictionary<string, ColumnInfo> DictColumnInfo { get; set; }

        public List<string> DBNames { get; set; }

        public ColumnInfo this[string tableName, string columnName]
        {
            get
            {
                ColumnInfo colInfo;
                var strKey = (tableName + "@" + columnName).ToLower();
                DictColumnInfo.TryGetValue(strKey, out colInfo);
                return colInfo;
            }
        }

        public List<string> this[string tableName]
        {
            get
            {
                tableName = (tableName ?? string.Empty).ToLower();
                List<string> colNames;
                TableColumnNameDict.TryGetValue(tableName, out colNames);
                return colNames;
            }
        }

        public bool Refresh()
        {
            try
            {
                string dbSql = "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA order by  SCHEMA_NAME asc";
                DBNames = Db.ReadList<string>(dbSql);
                DBNames.Sort();

                string strSql = string.Format("SELECT table_name name,TABLE_COMMENT value FROM INFORMATION_SCHEMA.TABLES WHERE table_type='base table' and  table_schema = '{0}' order by table_name asc ", DBName);                
                this.TableComments = Db.ReadNameValues(strSql);

                if (TableComments != null && TableComments.Count > 0)
                {
                    this.TableNames = TableComments.AllKeys.ToList();

                    this.TableInfoDict = new Dictionary<string, TableInfo>();
                    this.TableColumnNameDict = new Dictionary<string, List<string>>();
                    this.TableColumnInfoDict = new Dictionary<string, List<ColumnInfo>>();
                    this.TableColumnComments = new Dictionary<string, NameValueCollection>();

                    this.DictColumnInfo = new Dictionary<string, ColumnInfo>();
                    foreach (var tableName in TableNames)
                    {
                        TableInfo tabInfo = new TableInfo();
                        tabInfo.TableName = tableName;
                        tabInfo.TabComment = TableComments[tableName];

                        //strSql = "SHOW FULL COLUMNS FROM " + tableName;

                        strSql = @"select ORDINAL_POSITION as Colorder,Column_Name as ColumnName,data_type as TypeName,COLUMN_COMMENT as DeText,
(case when data_type = 'float' or data_type = 'double' or data_type = 'decimal' then  NUMERIC_PRECISION else CHARACTER_MAXIMUM_LENGTH end ) as length,
NUMERIC_SCALE as Scale,( case when EXTRA='auto_increment' then 1 else 0 end) as IsIdentity,(case when COLUMN_KEY='PRI' then 1 else 0 end) as IsPK,
(case when IS_NULLABLE = 'NO' then 0 else 1 end)as CanNull,COLUMN_DEFAULT as DefaultVal
from information_schema.columns where table_schema = ?DBName and table_name = ?tableName order by ORDINAL_POSITION asc";

                        tabInfo.Colnumns = Db.QueryTable(strSql, new { DBName = DBName, tableName = tableName.ToLower() }).ConvertToListObject<ColumnInfo>();
                        
                        List<string> lstColName = new List<string>();
                        NameValueCollection nvcColDeText = new NameValueCollection();
                        foreach (ColumnInfo colInfo in tabInfo.Colnumns)
                        {
                            lstColName.Add(colInfo.ColumnName);
                            nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText);

                            var strKey = (tableName + "@" + colInfo.ColumnName).ToLower();
                            DictColumnInfo.Add(strKey, colInfo);

                            if (colInfo.IsPK)
                            {
                                tabInfo.PriKeyColName = colInfo.ColumnName;
                                if (colInfo.IsIdentity)
                                {
                                    tabInfo.PriKeyType = PrimaryKeyType.AUTO;
                                }
                                else
                                {
                                    tabInfo.PriKeyType = PrimaryKeyType.SET;
                                }
                            }
                        }

                        TableInfoDict.Add(tableName, tabInfo);
                        TableColumnNameDict.Add(tableName, lstColName);
                        TableColumnInfoDict.Add(tableName, tabInfo.Colnumns);
                        TableColumnComments.Add(tableName, nvcColDeText);
                    }
                }
            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }


        #region MySql 获取列信息
        private List<ColumnInfo> MySqlReadColInfo(string strSql)
        {
            List<ColumnInfo> lstCols = new List<ColumnInfo>();
            DbDataReader reader = null;
            reader = Db.Reader(strSql);
            int colorder = 1;
            while (reader.Read())
            {
                ColumnInfo colInfo = new ColumnInfo();
                colInfo.Colorder = colorder;
                colInfo.ColumnName = reader["Field"].ToString();

                {
                    string typename = reader["Type"].ToString();
                    string len = "", pre = "", scal = "";
                    TypeNameProcess(typename, out typename, out len, out pre, out scal);

                    colInfo.TypeName = typename;
                    colInfo.Length = len.ChangeType<int?>((int?)null);
                    colInfo.Scale = scal.ChangeType<int?>((int?)null);
                }

                colInfo.IsPK = (reader["Key"].ToString() == "PRI") ? true : false;

                colInfo.CanNull = (reader["Null"].ToString() == "YES") ? true : false;

                colInfo.DefaultVal = reader["Default"].ToString();

                colInfo.DeText = reader["Comment"].ToString();

                colInfo.IsIdentity = (reader["Extra"].ToString() == "auto_increment") ? true : false;

                lstCols.Add(colInfo);

                colorder++;
            }

            if (reader != null && !reader.IsClosed)
            {
                reader.Close();
            }

            return lstCols;
        }

        //对类型名称 解析
        private void TypeNameProcess(string strName, out string TypeName, out string Length, out string Preci, out string Scale)
        {
            TypeName = strName;
            Length = string.Empty;
            Preci = string.Empty;
            Scale = string.Empty;

            if (strName.Contains("("))
            {
                if (!strName.Contains(","))
                {
                    TypeName = Regex.Replace(strName, @"(\w+)\((\d+)\)", "$1", RegexOptions.Compiled);
                    Length = Regex.Replace(strName, @"(\w+)\((\d+)\)", "$2", RegexOptions.Compiled);
                }
                else
                {
                    TypeName = Regex.Replace(strName, @"(\w+)\((\d+)\)", "$1", RegexOptions.Compiled);
                    Length = Regex.Replace(strName, @"(\w+)\((\d+),(\d+)\)", "$2", RegexOptions.Compiled);
                    Scale = Regex.Replace(strName, @"(\w+)\((\d+),(\d+)\)", "$3", RegexOptions.Compiled);
                }
            }
        }
        #endregion


        /// <summary>
        /// 查询数据库中的所有表是否 的都用 MyISAM 存储引擎 存储
        /// </summary>
        public bool IsAllMyISAM
        {
            get
            {
                string strSql = string.Format("select case when ((SELECT count(1) FROM information_schema.tables where table_type='base table' and TABLE_SCHEMA='{0}' )= (SELECT count(1) FROM information_schema.tables where table_type='base table' and TABLE_SCHEMA = '{0}' and ENGINE = 'MyISAM')) then true else FALSE end as res", DBName);
                return Db.Single<bool>(strSql, false);
            }
        }

       

        public Dictionary<string, DateTime> GetTableStruct_Modify()
        {
            string strSql = string.Format("SELECT TABLE_NAME name,UPDATE_TIME modify_date FROM information_schema.tables where TABLE_SCHEMA='{0}' and table_type='base table' and update_time is not null ORDER BY UPDATE_TIME asc", DBName);
            return Db.ReadDictionary<string, DateTime>(strSql);
        }


        public bool IsExistTable(string tableName)
        {
            tableName = (tableName ?? string.Empty).ToLower();
            return TableNames.Contains(tableName);
        }

        public bool IsExistColumn(string tableName, string columnName)
        {
            var strKey = (tableName + "@" + columnName).ToLower();
            return DictColumnInfo.ContainsKey(strKey);
        }

        public string GetColumnComment(string tableName, string columnName)
        {
            ColumnInfo colInfo = null;
            var strKey = (tableName + "@" + columnName).ToLower();
            DictColumnInfo.TryGetValue(strKey, out colInfo);
            return colInfo?.DeText;
        }


        public string GetTableComment(string tableName)
        {
            tableName = (tableName ?? string.Empty).ToLower();
            return TableComments[tableName];
        }

        public List<ColumnInfo> GetColumns(string tableName)
        {
            tableName = (tableName ?? string.Empty).ToLower();
            List<ColumnInfo> colInfos = null;
            TableColumnInfoDict.TryGetValue(tableName, out colInfos);
            return colInfos;
        }

        public bool SetTableComment(string tableName, string comment)
        {
            tableName = (tableName ?? string.Empty).ToLower();
            if (!TableNames.Contains(tableName))
            {
                //throw new ArgumentException("该表不存在！" + tableName, "tableName");
                return false;
            }

            string upsert_sql = string.Empty;
            comment = (comment ?? string.Empty).Replace("'", "");
            try
            {
                upsert_sql = "ALTER TABLE " + tableName + " COMMENT='" + comment + "';";
                Db.ExecSql(upsert_sql);

                TableComments[tableName] = comment;

                var tabInfo = TableInfoDict[tableName];
                tabInfo.TabComment = comment;
                TableInfoDict[tableName] = tabInfo;

            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }


        public  bool SetColumnComment(string tableName, string columnName, string comment)
        {
            tableName = (tableName ?? string.Empty).ToLower();
            columnName = (columnName ?? string.Empty).ToLower();

            if (!TableNames.Contains(tableName))
            {
                //throw new ArgumentException("该表不存在！" + tableName, "tableName");
                return false;
            }

            if (!DictColumnInfo.ContainsKey(tableName + "@" + columnName))
            {
                //throw new ArgumentException(tableName + "表不存在" + columnName + "列！", "columnName");
                return false;
            }

            string upsert_sql = string.Empty;
            comment = (comment ?? string.Empty).Replace("'", "");

            try
            {
                //mysql修改字段注释方法:http://blog.sina.com.cn/s/blog_72aace390102uwgg.html
                string selsql = "use information_schema;select column_Type from COLUMNS where table_name = '" + tableName + "' and column_name = '" + columnName + "';";
                string col_type = Db.Single<string>(selsql,string.Empty);
                upsert_sql = "use " + DBName + ";ALTER TABLE " + tableName + " MODIFY COLUMN " + columnName + " " + col_type + " COMMENT '" + comment + "';";
                Db.ExecSql(upsert_sql);

                List<ColumnInfo> lstColInfo = TableColumnInfoDict[tableName];

                NameValueCollection nvcColDesc = new NameValueCollection();
                lstColInfo.ForEach(t =>
                {
                    if (t.ColumnName.Equals(columnName))
                    {
                        t.DeText = comment;
                    }
                    nvcColDesc.Add(t.ColumnName, t.DeText);
                });

                TableColumnInfoDict.Remove(tableName);
                TableColumnInfoDict.Add(tableName, lstColInfo);

                TableColumnComments.Remove(tableName);
                TableColumnComments.Add(tableName, nvcColDesc);

                var strKey = (tableName + "@" + columnName);
                ColumnInfo colInfo = DictColumnInfo[strKey];
                colInfo.DeText = comment;
                DictColumnInfo[strKey] = colInfo;
            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }

        public bool DropTable(string tableName)
        {
            tableName = (tableName ?? string.Empty).ToLower();

            if (!TableNames.Contains(tableName))
            {
                throw new ArgumentException("该表不存在！" + tableName, "tableName");
            }

            string drop_sql = string.Empty;
            try
            {

                drop_sql = "drop table " + tableName;
                Db.ExecSql(drop_sql);

                this.TableComments.Remove(tableName);

                this.TableNames = TableComments.AllKeys.ToList();

                this.TableInfoDict.Remove(tableName);
                this.TableColumnInfoDict.Remove(tableName);
                this.TableColumnComments.Remove(tableName);

                var lstColName = TableColumnNameDict[tableName];

                foreach (var colName in lstColName)
                {
                    var strKey = (tableName + "@" + colName).ToLower();
                    this.DictColumnInfo.Remove(strKey);
                }

                this.TableColumnNameDict.Remove(tableName);

            }
            catch (Exception ex)
            {

                return false;
            }
            return true;
        }


        public bool DropColumn(string tableName, string columnName)
        {
            tableName = (tableName ?? string.Empty).ToLower();
            columnName = (columnName ?? string.Empty).ToLower();

            if (!TableNames.Contains(tableName))
            {
                throw new ArgumentException("该表不存在！" + tableName, "tableName");
            }

            var strKey = (tableName + "@" + columnName).ToLower();

            if (!DictColumnInfo.ContainsKey(strKey))
            {
                throw new ArgumentException(tableName + "表不存在" + columnName + "列！", "columnName");
            }

            try
            {
                string drop_sql = "alter table {0} drop column {1}";
                drop_sql = string.Format(drop_sql, tableName, columnName);
                Db.ExecSql(drop_sql);

                this.DictColumnInfo.Remove(strKey);

                var nvc = TableColumnComments[tableName];
                nvc.Remove(columnName);
                TableColumnNameDict[tableName] = nvc.AllKeys.ToList();

                var lstColInfo = TableColumnInfoDict[tableName];
                ColumnInfo curColInfo = null;
                lstColInfo.ForEach(t =>
                {
                    if (t.ColumnName.Equals(columnName))
                    {
                        curColInfo = t;

                        //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。
                        if (curColInfo.IsPK)
                        {
                            var tabInfo = TableInfoDict[tableName];
                            tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN;
                            tabInfo.PriKeyColName = null;
                            TableInfoDict[tableName] = tabInfo;
                        }
                        return;
                    }
                });
                lstColInfo.Remove(curColInfo);
                TableColumnInfoDict[tableName] = lstColInfo;

            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }

    }
}
